/**
 * @file app_acf.c
 * @brief 温控氟泵传感器应用程序
 * @author wangh (wanghuan3037@fiberhome.com)
 * @version 1.0
 * @date 2021-03-05
 * @copyright Copyright (c) 2021  烽火通信
 */ 
#include "project_board.h"
#include "bsp_uart_v2.h"
#include "bsp_adc.h"
#include "bsp_tim.h"
#include "74HC4051.h"
#include "wh_mb.h"

#undef LOG_LEVEL_
#define LOG_LEVEL_ 	LOG_LVL_VERBOSE
#include "bsp_log.h"



Gpio_t rs485_dir_obj;   /* 485方向 */
Gpio_t bus_set1_obj;    /* 485地址拨码 */
Gpio_t bus_set2_obj;
Gpio_t bus_set3_obj;
Gpio_t bus_set4_obj;
Gpio_t bus_set5_obj;

Gpio_t di1_obj;         /* 干接点 */
Gpio_t di2_obj;
Gpio_t di3_obj;
Gpio_t di4_obj;

Gpio_t relay1_obj;      /* 继电器输出 */
Gpio_t relay2_obj;      /* 继电器输出 */

Adc_t adc1_obj;         /* adc及其复用 */
Adc_t adc2_obj;
Hc4051_t adc1_multiplex_obj;
Hc4051_t adc2_multiplex_obj;

MB_t mb_obj;    ///< modbus协议处理对象


static void Timer0_PwmInit(void);
static void Pwm_Dac_SetValue(uint16_t dac_value);

static uint16_t g_dac_value;

/**
 * @brief 温控氟泵MODBUS寄存器处理
 * @param[in]  reg_addr     寄存器地址
 * @param[in/out]  data     读取/写入数据
 * @param[in]  rw_flag      读写标志，0：Read，非0：Write
 * @param[in]  user_data    用户数据
 * @return uint8_t 执行结果
 * - @b Sucess : 0 \n
 * - @b Fail ：异常码（01~06）
 */
static uint8_t acf_mb_regHandle( uint16_t reg_addr, uint8_t *data, uint8_t rw_flag, void *user_data )
{
    uint16_t reg_value = 0;

    if (rw_flag)    /* 寄存器读写功能判断 */
    {
        if (reg_addr > 0x0203)
            return 1;   /* 不支持写 */
        reg_value = (data[0]<<8 | data[1]);
    }

    switch (reg_addr)
    {
    case 0x0201:    /* 继电器1 */
    {
        if (rw_flag)
        {
            if (reg_value == 0)
                GpioWrite( &relay1_obj, 0 );
            else if (reg_value == 1)
                GpioWrite( &relay1_obj, 1 );
            else
                return 3;   /* 非法数据值 */
        }
        else
        {
            data[0] = 0x00;
            data[1] = GpioRead( &relay1_obj );
        }
    } break;

    case 0x0202:    /* 继电器2 */
    {
        if (rw_flag)
        {
            if (reg_value == 0)
                GpioWrite( &relay2_obj, 0 );
            else if (reg_value == 1)
                GpioWrite( &relay2_obj, 1 );
            else
                return 3;   /* 非法数据值 */
        }
        else
        {
            data[0] = 0x00;
            data[1] = GpioRead( &relay2_obj );
        }
    } break;

    case 0x0203:    /* PWM-DAC输出 */
    {
        if (rw_flag)
        {
            g_dac_value = reg_value;
            Pwm_Dac_SetValue( reg_value );
        }
        else
        {
            data[0] = (uint8_t)(g_dac_value>>8);
            data[1] = (uint8_t)(g_dac_value&0xFF);
        }
    } break;

    case 0x0204:    /* 输入干接点1 */
    {
        data[0] = 0x00;
        data[1] = GpioRead( &di1_obj );
    } break;

    case 0x0205:    /* 输入干接点2 */
    {
        data[0] = 0x00;
        data[1] = GpioRead( &di2_obj );
    } break;

    case 0x0206:    /* 输入干接点3 */
    {
        data[0] = 0x00;
        data[1] = GpioRead( &di3_obj );
    } break;

    case 0x0207:    /* 输入干接点4 */
    {
        data[0] = 0x00;
        data[1] = GpioRead( &di4_obj );
    } break;

    case 0x0208:    /* ADC输入1 */
    case 0x0209:    /* ADC输入2 */
    case 0x020A:    /* ADC输入3 */
    case 0x020B:    /* ADC输入4 */
    case 0x020C:    /* ADC输入5 */
    case 0x020D:    /* ADC输入6 */
    case 0x020E:    /* ADC输入7 */
    case 0x020F:    /* ADC输入8 */
    {
        HC4051_Select( &adc1_multiplex_obj, reg_addr - 0x0208 );
        reg_value = (uint16_t)(AdcReadChannelVol(&adc1_obj) * 100); /* 比例关系没有计算 */

        data[0] = (uint8_t)(reg_value>>8);
        data[1] = (uint8_t)(reg_value&0xFF);
    } break;

    case 0x0210:    /* ADC输入9 */
    case 0x0211:    /* ADC输入10 */
    case 0x0212:    /* ADC输入11 */
    case 0x0213:    /* ADC输入12 */
    {
        HC4051_Select( &adc2_multiplex_obj, reg_addr - 0x0210 );
        reg_value = (uint16_t)(AdcReadChannelVol(&adc2_obj) * 100); /* 比例关系没有计算 */

        data[0] = (uint8_t)(reg_value>>8);
        data[1] = (uint8_t)(reg_value&0xFF);
    } break;

    default:
        return 2;
    }

    return 0;
}

/**
 * @brief 温控氟泵传感板RS485协议处理
 * @param[in]  pmb      modbus协议处理对象
 * @param[in]  data     处理返回数据
 * @return uint8_t 执行结果
 * - @b Sucess : 0 \n
 * - @b Fail ：异常码（01~06）
 */
static uint8_t acf_mb_handle( MB_t *pmb, uint8_t *data )
{
    uint8_t i, ret;
    log_d("mb cmd(%02X) reg_start(%04X) reg_len(%d)\r\n", pmb->pfrm.cmd, pmb->pfrm.addr_start, pmb->pfrm.addr_len);

    if (pmb->pfrm.addr == 0xFF)     /* 广播帧处理 */
    {
        if (pmb->pfrm.addr_start == 0x0401)
            NVIC_SystemReset();
        else
            return 2;   /* 非法数据地址 */
    }

    if (pmb->pfrm.cmd == 0x03)      /* 读保持寄存器 */
    {
        if (data == NULL)
            return 4;   /* 从设备故障（不会走到这个地方） */

        for (i = 0; i < pmb->pfrm.addr_len; i++)
        {
            ret = acf_mb_regHandle( pmb->pfrm.addr_start + i, &data[i*2], 0, NULL );
            if (ret != 0)
                return ret;
        }
    }
    else if (pmb->pfrm.cmd == 0x06) /* 写单个寄存器 */
    {
        ret = acf_mb_regHandle( pmb->pfrm.addr_start, data, 1, NULL );
        if (ret != 0)
            return ret;
    }
    else if (pmb->pfrm.cmd == 0x10) /* 写多个寄存器 */
    {
        for (i = 0; i < pmb->pfrm.addr_len; i++)
        {
            ret = acf_mb_regHandle( pmb->pfrm.addr_start + i, &data[i*2], 1, NULL );
            if (ret != 0)
                return ret;
        }
    }
    else
        return 1;   /* 非法功能 */

    return 0;
}

static void rs485_send( uint8_t *buffer, uint16_t size )
{
    GpioWrite( &rs485_dir_obj, 1 );
    UartSend( UART_2, buffer, size );
    GpioWrite( &rs485_dir_obj, 0 );
}

/**
 * @brief RS485接收测试任务
 */
void mb_recv_task(void *para)
{
    log_i("mb_recv_task start... \r\n");

    while(1)
    {
        uint8_t buffer[32];
        memset(buffer, 0, 32);
        int32_t recv_len = UartRecv( UART_2, buffer, 32, portMAX_DELAY );
        if( recv_len > 0)
        {
            if (recv_len > 32)
                recv_len = 32;

            log_hexdump( buffer, recv_len );
            mb_obj.frm_handle( &mb_obj, buffer, recv_len );
        }
        else
        {
            log_d("uart recv timeout\r\n");
        }

        vTaskDelay(1);
    }
}


/**
 * @brief 应用启动
 */
void acf_task(void *para)
{
    log_i("acf_task start... \r\n");

    /* 干接点、继电器输出 */
    GpioInit( &di1_obj, DI1_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &di2_obj, DI2_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &di3_obj, DI3_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &di4_obj, DI4_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );

    GpioInit( &relay1_obj, RELAY1_PIN, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &relay2_obj, RELAY2_PIN, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );


    /* adc及其复用初始化 */
    AdcInitChannel( &adc1_obj, ADC1_PIN );
    AdcInitChannel( &adc2_obj, ADC2_PIN );
    HC4051_Init( &adc1_multiplex_obj, ADC1_A0_PIN, ADC1_A1_PIN, ADC1_A2_PIN, ADC1_AEN_PIN );
    HC4051_Init( &adc2_multiplex_obj, ADC2_A0_PIN, ADC2_A1_PIN, ADC2_A2_PIN, ADC2_AEN_PIN );

    Timer0_PwmInit();

    /* 拨码开关初始化 */
    GpioInit( &bus_set1_obj, BUS_SET1_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &bus_set2_obj, BUS_SET2_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &bus_set3_obj, BUS_SET3_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &bus_set4_obj, BUS_SET4_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &bus_set5_obj, BUS_SET5_PIN, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );

    /* MODBUS 初始化 */
    uint8_t rs485_addr = (uint8_t)(~(GpioRead(&bus_set1_obj) | (GpioRead(&bus_set2_obj)<<1) | (GpioRead(&bus_set3_obj)<<2) | (GpioRead(&bus_set4_obj)<<3) | (GpioRead(&bus_set5_obj)<<4)) & 0x1F);
    log_i("rs485_addr = %d\r\n", rs485_addr);

    mb_init( &mb_obj, rs485_addr, rs485_send, acf_mb_handle );

    /* RS485初始化 */
#if (LOG_UART != 2)
    GpioInit( &rs485_dir_obj, RS485_DIR_PIN, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    if( BSP_SUCCESS != UartInit( UART_2, UART2_TX_PIN, UART2_RX_PIN, 128 ))
        log_e("Uart2 Init fail !\r\n");
    else
    {
        UartConfig( UART_2, RX_TX, 9600, UART_8_BIT, UART_1_STOP_BIT, NO_PARITY, NO_FLOW_CTRL );
        xTaskCreate( mb_recv_task, "mb_recv", 256, NULL, 14, NULL);
    }
#endif

    vTaskDelete( NULL );
}


void application_start(void)
{
    xTaskCreate( acf_task, "acf_task", 256, NULL, 13, NULL );
}



#if SHELL_TASK_EN
#include "nr_micro_shell.h"
/**
 * @brief 电机测试指令
 */
void acf_test_cmd(char argc, char *argv)
{
    if (argc >= 2)
    {
        if (!strcmp("relay", &argv[argv[1]])) /* 继电器控制 acf relay 1 1 */
        {
            if(argc < 4)
            {
                shell_printf("acf relay need more arguments!\r\n");
                return;
            }
            if (atoi(&argv[argv[2]]) == 1)
                GpioWrite( &relay1_obj, (uint8_t)atoi(&argv[argv[2]]) );
            else if (atoi(&argv[argv[2]]) == 2)
                GpioWrite( &relay2_obj, (uint8_t)atoi(&argv[argv[2]]) );
            else
                shell_printf("acf relay only support 1 or 2!\r\n");
        }

        else if (!strcmp("getdi", &argv[argv[1]]))  /* 干接点状态读取 acf getdi */
        {
            shell_printf("DI = %d  %d  %d  %d\r\n", GpioRead(&di1_obj), GpioRead(&di2_obj), GpioRead(&di3_obj), GpioRead(&di4_obj) );
        }

        else if (!strcmp("getadc", &argv[argv[1]])) /* 模拟输入读取 acf getadc 1~12 */
        {
            if(argc < 3)
            {
                shell_printf("acf getadc need more arguments!\r\n");
                return;
            }

            uint8_t adc_ch = atoi(&argv[argv[2]]);
            char str_tmp[20];
            if ( (adc_ch > 0) && (adc_ch <= 8) )
            {
                HC4051_Select( &adc1_multiplex_obj, adc_ch - 1 );
                snprintf( str_tmp, 20, "%.3f", AdcReadChannelVol(&adc1_obj) );
                shell_printf("adc(%d) = %s\r\n", adc_ch, str_tmp );
            }
            if ( (adc_ch > 8) && (adc_ch <= 12) )
            {
                HC4051_Select( &adc2_multiplex_obj, adc_ch - 9 );
                snprintf( str_tmp, 20, "%.3f", AdcReadChannelVol(&adc2_obj) );
                shell_printf("adc(%d) = %s\r\n", adc_ch, str_tmp );
            }
            else
            {
                shell_printf("acf getadc support 1 to 12!\r\n");
            }
        }

#if (LOG_UART != 2)
        else if (!strcmp("rs485", &argv[argv[1]]))  /* 485发送测试 acf rs485 hello */
        {
            if(argc < 3)
            {
                shell_printf("acp led need more arguments!\r\n");
                return;
            }
            GpioWrite( &rs485_dir_obj, 1 );
            UartPrintf( UART_2, "%s\r\n", &argv[argv[2]] );
            GpioWrite( &rs485_dir_obj, 0 );
        }
#endif

    }
    else
    {
        shell_printf("acf need more arguments!\r\n");
    }
}
NR_SHELL_CMD_EXPORT(acf, acf_test_cmd);
#endif  /* SHELL_TASK_EN */


static void Timer0_PwmInit(void)
{
    rcu_periph_clock_enable(RCU_GPIOA);
    gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_9);

    rcu_periph_clock_enable(RCU_TIMER0);	
    rcu_apb2_clock_config(RCU_APB2_CKAHB_DIV1);	   
    timer_deinit(TIMER0);
    //timebase config
    timer_primary_output_config(TIMER0,ENABLE);
    timer_parameter_struct basetime_struct;
    basetime_struct.clockdivision=TIMER_CKDIV_DIV1;
    basetime_struct.counterdirection=TIMER_COUNTER_UP;
    basetime_struct.period=99;
    basetime_struct.prescaler=17;
    basetime_struct.alignedmode=TIMER_COUNTER_EDGE;
    basetime_struct.repetitioncounter=0;
    timer_init(TIMER0,&basetime_struct);
    
    //pwm mode config
    timer_oc_parameter_struct timer_oc_struct;
    timer_channel_output_mode_config(TIMER0,TIMER_CH_1,TIMER_OC_MODE_PWM0);
    timer_oc_struct.outputstate=TIMER_CCX_ENABLE;
    timer_oc_struct.outputnstate=TIMER_CCXN_DISABLE;
    timer_oc_struct.ocidlestate=TIMER_OC_IDLE_STATE_LOW;
    timer_oc_struct.ocnidlestate=TIMER_OCN_IDLE_STATE_LOW;
    timer_oc_struct.ocpolarity=TIMER_OC_POLARITY_HIGH;
    timer_oc_struct.ocnpolarity=TIMER_OC_POLARITY_HIGH;
    timer_channel_output_config(TIMER0,TIMER_CH_1,&timer_oc_struct);
    timer_channel_control_shadow_config(TIMER0,DISABLE);
    timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_1,0);
    timer_auto_reload_shadow_enable(TIMER0);
    
    timer_enable(TIMER0);//使能定时器0
}

static void Pwm_Dac_SetValue(uint16_t dac_value)
{
    float temp=dac_value*0.01;
    uint8_t pwm_duty=(temp/10)*100;
    //设置pwm占空比值
    timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_1,pwm_duty);
}


/*********** (C) COPYRIGHT 2021 FiberHome *****END OF FILE****/
